package com.helloscala.web
package snippet

import scala.xml.NodeSeq

import net.liftweb.http.{Templates, SHtml, S, DispatchSnippet}
import net.liftweb.util.Helpers._
import net.liftweb.util.ClearNodes
import net.liftweb.common.{Box, Loggable, Failure, Full}
import net.liftweb.http.js.{JE, JsCmds, JsCmd}

import org.bson.types.ObjectId

import yangbajing.util.Imports.Y

import com.helloscala.helper.{MarkdownHelpers, HelloHelpers}
import com.helloscala.model._

object TopicSnippet extends DispatchSnippet with Loggable {
  def dispatch = {
    case "panel" => panel
    case "show" => show
    case "edit" => edit
    case "detailTitle" => detailTitle
    case "authorInfo" => authorInfo
    case "accountTopics" => accountTopics
    case "popularPanel" => popularPanel
    case "tagPanel" => tagPanel
    case "comments" => comments
    case "comment" => comment
  }

  def panel(nodeSeq: NodeSeq): NodeSeq = {
    val panelClass = S.attr("panelClass") or H.param("panel_class") openOr ""
    val author = H.param("user_id")
    val tagId = H.param("tag_id") or S.attr("tagId")
    val panelTitle = tagId.map(v => "标签：" + v) or S.param("panel_title") or S.attr("panelTitle") openOr ""
    val topicTitle = S.attr("topicTitle") or H.param("topic_title")
    val curPage = S.param("cur_page") or S.attr("curPage") flatMap (asInt _) openOr 1
    val limit = S.param("limit") or S.attr("limit") flatMap (asInt(_)) openOr H.defaultLimit

    val pagination = MTopic.pagination(curPage, limit, author = author, title = topicTitle, tagId = tagId)

    val topicNewSel = W.theAccount.is match {
      case Full(account) => "@topic-new [href]" #> W.hrefTopicNew(account.id)
      case _ => "@topic-new" #> ClearNodes
    }

    val topicsSel =
      "li" #> pagination.page.map {
        topic =>
          val userHref = W.hrefUser(topic.author)
          val imgSrc = W.href("/assets/img/logo-1-100.png")
          val articleHref = W.hrefTopic(topic.author, topic.slug.getOrElse(topic.id))
          val meta =
            <span>
              <a href={userHref}>
                {topic.author}
              </a>{HelloHelpers.timeDescription(topic.created_at)}
            </span>

          ".topic-avatar" #> (
            "a [href]" #> userHref &
              "img [src]" #> imgSrc
            ) &
            ".topic-info" #> (
              "a [href]" #> articleHref &
                "a *" #> topic.title &
                ".text-muted" #> meta
              )
      }

    val paginationSel =
      S.attr("showPagination").flatMap(asBoolean) match {
        case Full(true) => ".pagination" #> pagination.pagination("")
        case _ => ".pagination" #> ClearNodes
      }

    val cssSel =
      ".panel [class+]" #> panelClass &
        ".panel-title *" #> panelTitle &
        "#topic-total *" #> pagination.total &
        topicNewSel &
        ".topics" #> topicsSel &
        paginationSel

    cssSel(nodeSeq)
  }

  def edit(nodeSeq: NodeSeq): NodeSeq = {
    if (S.param("action").map(_ != "new").getOrElse(true) && W.reqTopic.isEmpty) {
      return <h1 class="text-danger">
        {s"主题未找到"}
      </h1>
    }

    W.theAccount.is match {
      case Full(account) if W.equalSiteId(account.id) && W.reqTopic.map(_.author == account.id).openOr(true) =>
        _edit(nodeSeq, account)
      case _ =>
        <h1 class="text-danger">没有编辑权限</h1>
    }
  }

  private[this] def _edit(nodeSeq: NodeSeq, account: Account): NodeSeq = {
    val isAdd = W.reqTopic.is.isEmpty

    logger.debug("Topic Request Var: " + W.reqTopic.is)
    val req = W.reqTopic.is match {
      case Full(a) => new ReqTopic(a.id, a.author, a.title, a.content, a.description, a.slug, a.tagIds)
      case _ => new ReqTopic(ObjectId.get.toString, account.id, "", "", None, None, Nil)
    }

    logger.debug("Topic before edit req:\n" + req)

    val oldSlug: Box[String] = W.reqTopic.is.flatMap(_.slug)

    def funcSlug(v: String): JsCmd = {
      val slug = v.trim
      if (slug == "")
        JsCmds.Noop
      else if (oldSlug.exists(_ == v))
        JE.Call("formControlSuccess", "#control-topic-slug").cmd
      else if (MTopic.haveSlug(v))
        JE.Call("formControlError", "#control-topic-slug", "短名字已存在").cmd
      else
        JE.Call("formControlSuccess", "#control-topic-slug").cmd
    }

    def submitFunc(): JsCmd = {
      val ret = W.reqTopic.is match {
        case Full(a) => // UPDATE
          val topic = a.copy(author = req.author, title = req.title, description = req.description,
            content = req.content, slug = req.slug)
          MTopic.save(topic, req.removeTags(a.tagIds), req.newTags(a.tagIds))
        case _ => // INSERT
          val topic = MTopic(req.author, req.title, req.content, req.slug, 0, req.description)
          MTopic.insert(topic, req.tags)
      }
      ret match {
        case Full(a) =>
          JsCmds.Alert("保存主题成功") &
            JsCmds.RedirectTo(W.hrefTopicEdit(a.author, a.slug getOrElse a.id))

        case Failure(msg, e, _) =>
          JsCmds.Alert("保存文件失败！" + msg)

        case _ =>
          JsCmds.Alert("保存文件失败！")
      }
    }

    val cssSel =
      ".panel-title -*" #> (if (isAdd) "创建" else "编辑") &
        "@title" #> SHtml.text(req.title, req.title = _) &
        "@description" #> SHtml.text(req.description.getOrElse(""), v => req.description = Y.option(v)) &
        "@tags" #> SHtml.text(req.tags.mkString(","), v => req.tags = v.split(',').map(_.trim).toList) &
        "@slug" #> H.ajaxText(req.slug.getOrElse(""), funcSlug _)(v => req.slug = Y.option(v)) &
        "#topic-editor textarea" #> SHtml.textarea(req.content, req.content = _) &
        ".topic-preview *" #> W.reqTopic.is.map(_.contentNodeSeq).openOr(NodeSeq.Empty) &
        "#topic-button *" #> (if (isAdd) "立即创建" else "保存修改") &
        "@submit" #> SHtml.hidden(submitFunc)

    cssSel(nodeSeq)
  }

  def show(nodeSeq: NodeSeq): NodeSeq = {
    val cssSel =
      W.reqTopic.is match {
        case Full(topic) =>
          val tagsSel =
            if (topic.tags.nonEmpty)
              ".data-labels" #> (
                "a" #> topic.tags.map(tag =>
                  "a [href]" #> W.hrefTag(tag.tag) &
                    ".label *" #> tag.tag)
                )
            else
              ".data-labels" #> ClearNodes

          ".panel-heading" #> (
            ".panel-title *" #> topic.title &
              ".text-muted *" #> topic.description.getOrElse("") &
              (W.theAccount.is match {
                case Full(account) if topic.author == account.id =>
                  "a [href]" #> W.hrefTopicEdit(topic.author, topic.slug getOrElse topic.id)
                case _ =>
                  "a" #> ClearNodes
              })) &
            tagsSel &
            "#topic-content *" #> topic.contentNodeSeq

        case _ =>
          "*" #> <h1 class="text-danger">
            主题不存在
          </h1>
      }

    cssSel(nodeSeq)
  }

  def detailTitle(nodeSeq: NodeSeq): NodeSeq = {
    val cssSel =
      "* -*" #> W.reqTopic.is.map(_.title).openOr("创建主题")

    cssSel(nodeSeq)
  }

  def authorInfo(nodeSeq: NodeSeq): NodeSeq = {
    val author =
      W.reqTopic.is.map(_.author) or S.param("action").filter(_ == "new").flatMap(_ => W.theAccount.is.map(_.id))

    author match {
      case Full(v) => <div data-lift={"embed?what=/p/user/_user_info;user_id=" + v}></div>
      case _ => NodeSeq.Empty
    }
  }

  def accountTopics(nodeSeq: NodeSeq): NodeSeq = {
    val cssSel =
      for {
        userId <- S.attr("user_id").or(W.reqTopic.is.map(_.author)) ?~ "主题作者未找到"
      } yield {
        val limit = S attr "limit" flatMap asInt openOr 7
        val topics = MTopic.findAll(0, limit, author = Some(userId))

        "href=more [href]" #> W.hrefUser(userId) &
          ".list-group-item" #> topics.map {
            a =>
              ".badge *" #> MTopicComment.size(topicId = Some(a.id)) &
                "a [href]" #> W.hrefTopic(userId, a.slug getOrElse a.id) &
                "a *" #> a.title
          }
      }

    cssSel match {
      case Full(v) =>
        v(nodeSeq)

      case Failure(msg, e, _) =>
        val errorMsg = msg + " " + e.map(_.getLocalizedMessage).openOr("")
        logger.error(errorMsg)
        <h1 class="test-danger">
          {msg}
        </h1>

      case _ =>
        NodeSeq.Empty
    }
  }

  def popularPanel(nodeSeq: NodeSeq): NodeSeq = {
    val limit = S.attr("limit") flatMap (asInt) openOr 5

    val cssSel =
      ".list-group-item" #> MTopic.findAllPopular(limit).map(a =>
        "a [href]" #> W.hrefTopic(a.id) &
          "a *" #> a.title &
          ".badge *" #> a.comment_count
      )

    cssSel(nodeSeq)
  }

  def tagPanel(nodeSeq: NodeSeq): NodeSeq = {
    val limit = S.attr("limit") flatMap (asInt) openOr 5

    val cssSel =
      ".list-group-item" #> MTopic.findTagAll(0, limit).map(tag =>
        "a [href]" #> W.hrefTag(tag.tag) &
          "a *" #> tag.tag &
          ".badge *" #> tag.size
      )

    cssSel(nodeSeq)
  }

  def comments(nodeSeq: NodeSeq): NodeSeq = {
    val cssSel =
      for (topic <- W.reqTopic.is)
      yield {
        "@comment-size *" #> MTopicComment.size(topicId = Some(topic.id)) &
          "#topic-comments" #> (
            "li" #> MTopicComment.findLastByTopicId(topic.id, 15).map(c => _commentLi(c))
            )
      }

    cssSel map (_ apply nodeSeq) openOr NodeSeq.Empty
  }

  def comment(nodeSeq: NodeSeq): NodeSeq = {
    val cssSel =
      for (
        topic <- W.reqTopic.is;
        account <- W.theAccount.is
      ) yield {
        var comment = ""
        val creator = account.id

        "@comment" #> SHtml.textarea(comment, comment = _) &
          "@submit" #> SHtml.hidden(() => {
            MTopicComment.create(topic.id, creator, comment) match {
              case Full(c) => $("#topic-comments").append(_commentLi(c)).cmd
              case _ => JsCmds.Alert("评论失败")
            }
          })
      }

    cssSel map (_ apply nodeSeq) openOr NodeSeq.Empty
  }

  private[this] def _commentLi(c: MTopicComment): NodeSeq = {
    val avatarHref = W.hrefUser(c.creator)

    val cssSel =
      ".badge" #> ClearNodes &
        ".comment-avatar" #> (
          "a [href]" #> avatarHref
          ) &
        ".comment-info" #> (
          "p *" #> MarkdownHelpers.parseToNodeSeq(c.content) &
            ".text-muted *" #> "由 %s %s".format(c.creator, HelloHelpers.timeDescription(c.created_at))
          )

    cssSel(nodeCommentLi)
  }

  private[this] def panelTitle(tpe: String): String =
    tpe match {
      case "new" => "最新"
      case "popular" => "推荐"
      case v => v
    }

  def nodeCommentLi = Templates(List("p", "topic", "_comment_li")).openOrThrowException("/p/topic/_comment_li not found!!!")
}
